package crawlerv2;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;

public class Graph
{
    protected Vector<GraphNode> mNodes;
    protected Vector<GraphLink> mLinks;
    protected int mWidth;
    protected int mHeight;
    
    public Graph(int width, int height)
    {
        mNodes = new Vector<GraphNode>();
        mLinks = new Vector<GraphLink>();
        
        mWidth = width;
        mHeight = height;
        
        //CreateRandomGraph();
        //CreateFingerTableGraph();
    }
    
    public void AddLink(GraphLink newLink)
    {
    	if(!mLinks.contains(newLink))
    	{
    		mLinks.add(newLink);
    	}
    }
    
    private void CreateRandomGraph()
    {
        Random rand = new Random();
        for(int i = 0; i < 50; ++i)
        {
            float x = (rand.nextFloat() * (mWidth - 10) + 5) ;
            float y = (rand.nextFloat() * (mHeight - 10) + 5) ;
            float size = 10;
            mNodes.add(new GraphNode(x, y, size));
        }
        
        for(int i = 0; i < mNodes.size(); ++i) // add links for each node
        {
            int count = 0;
            for(int j = i+1; j < mNodes.size(); ++j)
            {
                if(rand.nextFloat() < 0.2f)
                {
                    GraphLink newLink = new GraphLink(mNodes.elementAt(i), mNodes.elementAt(j));
                    mLinks.add(newLink);
                    mNodes.elementAt(i).AddLink(newLink);
                    mNodes.elementAt(j).AddLink(newLink);
                    ++count;
                }
            }
        }
        
        for(int i = 0; i < mNodes.size(); ++i) // add links for each node
        {
            if(mNodes.elementAt(i).GetNumLinks() > 10)
            {
                mNodes.elementAt(i).SetColor(Color.GREEN);
            }
        }
    }
    
    private void CreateFingerTableGraph()
    {
        Random rand = new Random();
        for(int i = 0; i < 80; ++i)
        {
            float x = (rand.nextFloat() * (mWidth - 10) + 5) ;
            float y = (rand.nextFloat() * (mHeight - 10) + 5) ;
            float size = 10;
            mNodes.add(new GraphNode(x, y, size));
        }
        
        for(int i = 0; i < mNodes.size(); ++i) // add links for each node
        {
            int count = 0;
            for(int j = mNodes.size() / 2; j >= 1; j = j/2)
            {
                GraphLink newLink = new GraphLink(mNodes.elementAt(i), mNodes.elementAt((i+j)%mNodes.size()));
                mLinks.add(newLink);
                mNodes.elementAt(i).AddLink(newLink);
                mNodes.elementAt((i+j)%mNodes.size()).AddLink(newLink);
            }
        }
    }
    
    public void OrganizeNodesInCircle()
    {
        int numNodes = mNodes.size();
        if(numNodes < 1)
        {
        	return;
        }
        
        double circleDiv = (2.0 * Math.PI) / ((double)numNodes);
        Point2D.Float center = new Point2D.Float(mWidth/2, mHeight/2);
        float approxCircumf = Math.min((float)mWidth, (float)mHeight) * (float)Math.PI;
        float nodeSize = approxCircumf / (numNodes * 4);
        float distance = (Math.min((float)mWidth, (float)mHeight) - nodeSize) / 2;   
        distance -= 2.0f; // make a small border.
        
        for(int i = 0; i < numNodes; ++i)
        {
            float x = (float)Math.sin(circleDiv * (double)i) * distance;
            float y = (float)Math.cos(circleDiv * (double)i) * distance;
            mNodes.elementAt(i).SetPosition(new Point2D.Float(center.x + x, center.y + y));
            mNodes.elementAt(i).SetSize(nodeSize);
        }
    }
    
    public void Draw(Graphics2D g)
    {
        OrganizeNodesInCircle();
        g.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
        
        Iterator<GraphLink> linkIt = mLinks.iterator();
        while(linkIt.hasNext())
        {
            linkIt.next().Draw(g);
        }
        
        Iterator<GraphNode> nodeIt = mNodes.iterator();
        while(nodeIt.hasNext())
        {
            nodeIt.next().Draw(g);
        }
    }
    
    public void DrawToFile(String directory, String imageName)
    {
    	BufferedImage img = new BufferedImage(mWidth, mHeight, BufferedImage.TYPE_4BYTE_ABGR);
        Graphics2D gr = (Graphics2D)img.getGraphics();
        Draw(gr);
            
        File outputfile;
        String outputFileName = imageName + ".png";
        if(directory != null)
        {
        	outputfile = new File(directory, "temp_" + outputFileName);
        }
        else
        {
        	outputfile = new File("temp_" + outputFileName);
        }
        
        try
        {
           ImageIO.write(img, "png", outputfile);
        }
        catch(Exception e)
        {
            System.out.println("Caught exception saving to file: " + e);
        }
        
		// rename temp.png to outputFileName
		boolean renameSuccess = false;
		for(int i = 0; i < 10 && !renameSuccess; ++i)
		{
			if(directory != null)
			{
				renameSuccess = outputfile.renameTo(new File(directory, outputFileName));
			}
			else
			{
				renameSuccess = outputfile.renameTo(new File(outputFileName));
			}
		}
		if(!renameSuccess)
		{
			System.out.println("Couldn't rename the image file to " + outputFileName);
		}
    }
    
    public void DrawToFile(String imageName)
    {
    	DrawToFile(null, imageName);
    }
}
